diff --git a/doc/api/buffer.md b/doc/api/buffer.md
index 4488ae2ad9bf8d..3a04ad514b4d4e 100644
--- a/doc/api/buffer.md
+++ b/doc/api/buffer.md
@@ -908,6 +908,44 @@ console.log(buf2.toString());
 
 A `TypeError` will be thrown if `str` is not a string.
 
+### Class Method: Buffer.from(object[, offsetOrEncoding[, length]])
+<!-- YAML
+added: REPLACEME
+-->
+
+* `object` {Object} An object supporting `Symbol.toPrimitive` or `valueOf()`
+* `offsetOrEncoding` {number|string} A byte-offset or encoding, depending on
+  the value returned either by `object.valueOf()` or
+  `object[Symbol.toPrimitive]()`.
+* `length` {number} A length, depending on the value returned either by
+  `object.valueOf()` or `object[Symbol.toPrimitive]()`.
+
+For objects whose `valueOf()` function returns a value not strictly equal to
+`object`, returns `Buffer.from(object.valueOf(), offsetOrEncoding, length)`.
+
+For example:
+
+```js
+const buf = Buffer.from(new String('this is a test'));
+// <Buffer 74 68 69 73 20 69 73 20 61 20 74 65 73 74>
+```
+
+For objects that support `Symbol.toPrimitive`, returns
+`Buffer.from(object[Symbol.toPrimitive](), offsetOrEncoding, length)`.
+
+For example:
+
+```js
+class Foo {
+  [Symbol.toPrimitive]() {
+    return 'this is a test';
+  }
+}
+
+const buf = Buffer.from(new Foo(), 'utf8');
+// <Buffer 74 68 69 73 20 69 73 20 61 20 74 65 73 74>
+```
+
 ### Class Method: Buffer.isBuffer(obj)
 <!-- YAML
 added: v0.1.101
diff --git a/lib/buffer.js b/lib/buffer.js
index 984b558be69160..bfa8656483f602 100644
--- a/lib/buffer.js
+++ b/lib/buffer.js
@@ -176,12 +176,26 @@ Buffer.from = function(value, encodingOrOffset, length) {
   if (isAnyArrayBuffer(value))
     return fromArrayBuffer(value, encodingOrOffset, length);
 
+  if (value == null)
+    throw new TypeError(kFromErrorMsg);
+
+  if (typeof value === 'number')
+    throw new TypeError('"value" argument must not be a number');
+
+  const valueOf = value.valueOf && value.valueOf();
+  if (valueOf != null && valueOf !== value)
+    return Buffer.from(valueOf, encodingOrOffset, length);
+
   var b = fromObject(value);
   if (b)
     return b;
 
-  if (typeof value === 'number')
-    throw new TypeError('"value" argument must not be a number');
+  if (typeof value[Symbol.toPrimitive] === 'function') {
+    return Buffer.from(value[Symbol.toPrimitive]('string'),
+                       encodingOrOffset,
+                       length);
+  }
+
   throw new TypeError(kFromErrorMsg);
 };
 
diff --git a/test/parallel/test-buffer-from.js b/test/parallel/test-buffer-from.js
new file mode 100644
index 00000000000000..1d8ecb5533680a
--- /dev/null
+++ b/test/parallel/test-buffer-from.js
@@ -0,0 +1,57 @@
+'use strict';
+
+require('../common');
+const { deepStrictEqual, throws } = require('assert');
+const { Buffer } = require('buffer');
+const { runInNewContext } = require('vm');
+
+const checkString = 'test';
+
+const check = Buffer.from(checkString);
+
+class MyString extends String {
+  constructor() {
+    super(checkString);
+  }
+}
+
+class MyPrimitive {
+  [Symbol.toPrimitive]() {
+    return checkString;
+  }
+}
+
+class MyBadPrimitive {
+  [Symbol.toPrimitive]() {
+    return 1;
+  }
+}
+
+deepStrictEqual(Buffer.from(new String(checkString)), check);
+deepStrictEqual(Buffer.from(new MyString()), check);
+deepStrictEqual(Buffer.from(new MyPrimitive()), check);
+deepStrictEqual(Buffer.from(
+                  runInNewContext('new String(checkString)', {checkString})),
+                check);
+
+const err = new RegExp('^TypeError: First argument must be a string, Buffer, ' +
+                       'ArrayBuffer, Array, or array-like object\\.$');
+
+[
+  {},
+  new Boolean(true),
+  { valueOf() { return null; } },
+  { valueOf() { return undefined; } },
+  { valueOf: null },
+  Object.create(null)
+].forEach((input) => {
+  throws(() => Buffer.from(input), err);
+});
+
+[
+  new Number(true),
+  new MyBadPrimitive()
+].forEach((input) => {
+  throws(() => Buffer.from(input),
+         /^TypeError: "value" argument must not be a number$/);
+});